﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.DBIBase
{
    /// <summary>
    /// DbConnection连接池
    /// </summary>
    internal class DbConnectionPool : DbConnectionPoolAbs
    {
        /// <summary>
        /// 读连接对象集合池
        /// </summary>
        private readonly BlockingCollection<PrimitiveDbConnection> _readConPool;

        /// <summary>
        /// 写连接对象集合池
        /// </summary>
        private readonly BlockingCollection<PrimitiveDbConnection> _writeConPool;

        /// <summary>
        /// 连接池有效性ID
        /// </summary>
        private int _poolValidID;

        /// <summary>
        /// 连接池有效性ID锁
        /// </summary>
        private readonly object _poolValidIDLock = new object();

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="config">数据库配置</param>
        /// <param name="interaction">数据库交互实例</param>
        public DbConnectionPool(DatabaseConfig config, IDBInteraction interaction)
            : base(config, interaction)
        {
            this._readConPool = new BlockingCollection<PrimitiveDbConnection>(new ConcurrentStack<PrimitiveDbConnection>());
            this._writeConPool = new BlockingCollection<PrimitiveDbConnection>(new ConcurrentStack<PrimitiveDbConnection>());

            this._poolValidID = 1;
            this.InitDbConnection(config, this._poolValidID);
        }


        private void InitDbConnection(DatabaseConfig config, int poolValidID)
        {
            for (int i = 0; i < config.ReadConCount; i++)
            {
                this._readConPool.Add(base.CreateConnection(DBVisitType.R, poolValidID));
            }

            for (int i = 0; i < config.WriteConCount; i++)
            {
                this._writeConPool.Add(base.CreateConnection(DBVisitType.W, poolValidID));
            }
        }

        private int GetPoolValidID()
        {
            lock (this._poolValidIDLock)
            {
                return this._poolValidID;
            }
        }

        /// <summary>
        /// 重置连接
        /// </summary>
        protected override void PrimitiveResetDbConnection()
        {
            lock (this._poolValidIDLock)
            {
                const int millisecondsTimeout = 0;
                PrimitiveDbConnection pcon;
                if (this._config.ReadConCount >= DBConstant.ReadConCount)
                {
                    while (this._readConPool.TryTake(out pcon, millisecondsTimeout))
                    {
                        pcon.DbConnection.Close();
                    }
                }

                if (this._config.WriteConCount >= DBConstant.WriteConCount)
                {
                    while (this._writeConPool.TryTake(out pcon, millisecondsTimeout))
                    {
                        pcon.DbConnection.Close();
                    }
                }

                this._poolValidID++;
                this.InitDbConnection(base._config, this._poolValidID);
            }
        }

        /// <summary>
        /// 获取数据库访问连接对象
        /// </summary>
        /// <param name="visitType">数据库访问类型</param>
        /// <returns>数据库访问连接对象</returns>
        protected override PrimitiveDbConnection PrimitiveGetDbConnection(DBVisitType visitType)
        {
            PrimitiveDbConnection pcon;
            switch (visitType)
            {
                case DBVisitType.R:
                    if (this._config.ReadConCount < DBConstant.ReadConCount)
                    {
                        pcon = base.CreateConnection(DBVisitType.R, this.GetPoolValidID());
                    }
                    else
                    {
                        if (!this._readConPool.TryTake(out pcon, this._config.GetConTimeout))
                        {
                            throw new ApplicationException("从连接池获取读连接超时");
                        }
                    }
                    break;
                case DBVisitType.W:
                    if (this._config.WriteConCount < DBConstant.WriteConCount)
                    {
                        pcon = base.CreateConnection(DBVisitType.W, this.GetPoolValidID());
                    }
                    else
                    {
                        if (!this._writeConPool.TryTake(out pcon, this._config.GetConTimeout))
                        {
                            throw new ApplicationException("从连接池获取写连接超时");
                        }
                    }
                    break;
                default:
                    throw new NotSupportedException(string.Format("不支持的访问类型:{0}", visitType.ToString()));
            }

            try
            {
                if (pcon.DbConnection.State == ConnectionState.Closed)
                {
                    pcon.DbConnection.Open();
                }
            }
            catch (Exception ex)
            {
                this.ReleaseDbConnection(pcon, visitType);
                throw new ApplicationException("打开数据库连接异常", ex);
            }

            return pcon;
        }

        /// <summary>
        /// 释放数据库访问连接对象
        /// </summary>
        /// <param name="pcon">数据库访问连接对象</param>
        /// <param name="visitType">数据库访问类型</param>
        protected override void PrimitiveReleaseDbConnection(PrimitiveDbConnection pcon, DBVisitType visitType)
        {
            if (visitType == DBVisitType.R)
            {
                if (this._config.ReadConCount >= DBConstant.ReadConCount &&
                    this.GetPoolValidID() == pcon.PoolValidID)
                {
                    this._readConPool.Add(pcon);
                }
                else
                {
                    pcon.DbConnection.Close();
                }
            }
            else if (visitType == DBVisitType.W)
            {
                if (this._config.WriteConCount >= DBConstant.WriteConCount &&
                    this.GetPoolValidID() == pcon.PoolValidID)
                {
                    this._writeConPool.Add(pcon);
                }
                else
                {
                    pcon.DbConnection.Close();
                }
            }
            else
            {
                throw new NotSupportedException(string.Format("不支持的访问类型:{0}", visitType.ToString()));
            }
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        /// <param name="isDisposing">是否释放资源标识</param>
        protected override void Dispose(bool isDisposing)
        {
            try
            {
                foreach (var readCon in this._readConPool)
                {
                    try
                    {
                        readCon.DbConnection.Close();
                    }
                    catch (Exception exi)
                    {
                        ZLoger.Error(exi);
                    }
                }

                foreach (var writeCon in this._writeConPool)
                {
                    try
                    {
                        writeCon.DbConnection.Close();
                    }
                    catch (Exception exi)
                    {
                        ZLoger.Error(exi);
                    }
                }

                this._readConPool.Dispose();
                this._writeConPool.Dispose();
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }
        }
    }
}
